home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Utilities / RemoteCommand / Source / execServer.subproj / ExecScrollText.m < prev    next >
Encoding:
Text File  |  1993-06-10  |  10.6 KB  |  418 lines

  1. // -------------------------------------------------------------------------------------
  2. // ExecScrollText.m
  3. // -------------------------------------------------------------------------------------
  4. // Permission is granted to freely redistribute this source code, and to use fragments
  5. // of this code in your own applications if you find them to be useful.  This class,
  6. // along with the source code, come with no warranty of any kind, and the user assumes
  7. // all responsibility for its use.
  8. // -------------------------------------------------------------------------------------
  9.  
  10. #import <appkit/appkit.h>
  11. #import <libc.h>
  12. #import <mach/cthreads.h>
  13. #import <stdlib.h>
  14. #import <stdarg.h>
  15. #import <string.h>
  16. #import <pwd.h>
  17. #import <sys/types.h>
  18. #import <sys/wait.h>
  19. #import "ExecScrollText.h"
  20.  
  21. // -------------------------------------------------------------------------------------
  22. // null text attribute structure
  23. static NXColor            nullColor = { 0 };
  24. static textAttr_t        nullAttr = { (id)nil, 0 };
  25. #define    isNullAttr(X)    (!X.fontId && !X.colorMode)
  26.  
  27. // -------------------------------------------------------------------------------------
  28. // Object notified when command completes
  29. @interface Object(ExecScrollText_Delegate)
  30. - commandDidComplete:shellId withError:(int)errorCode;
  31. @end
  32.  
  33. // -------------------------------------------------------------------------------------
  34. // private methods
  35. @interface ExecScrollText(Private)
  36. - _setTextAttrFont:fontId color:(int)mode:(NXColor)color;
  37. - (BOOL)_appendTextToView:(const char*)buffer len:(int)len attr:(textAttr_t)tAttr;
  38. - _appendTextAndMakeVisible:(const char*)buffer attr:(textAttr_t)tAttr;
  39. - _stopCommand;
  40. - (int)_popen:(const char*)cmd;
  41. - (int)_pclose;
  42. - (void)_gotData;
  43. @end
  44.  
  45. // -------------------------------------------------------------------------------------
  46. /* convert color to gray */
  47. static float cvtColor2Gray(NXColor color)
  48. {
  49.     float    gray;
  50.     NXConvertColorToGray(color, &gray);
  51.     return gray;
  52. }
  53.  
  54. // -------------------------------------------------------------------------------------
  55. @implementation ExecScrollText
  56.  
  57. // -------------------------------------------------------------------------------------
  58. /* initializes the outlet (to be executed by main thread only!) */
  59. + newExecScrollText:anObject
  60. {
  61.     
  62.     /* init */
  63.     self             = [super new];
  64.     scrollView         = anObject;
  65.     textView         = [scrollView docView];
  66.     wasEditable         = [textView isEditable];
  67.     autoLf             = NO;
  68.     runAttr             = nullAttr;
  69.     cmdChild         = 0;
  70.     
  71.     /* set textView attributes */
  72.     //[textView setEditable:NO];
  73.     //[textView setMonoFont:NO];
  74.     
  75.     return self;
  76. }
  77.  
  78. /* return textView id (docView) */
  79. - docView
  80. {
  81.     return textView;
  82. }
  83.  
  84. /* return scroll view */
  85. - scrollView
  86. {
  87.     return scrollView;
  88. }
  89.  
  90. /* set delegate */
  91. - setDelegate:theDelegate
  92. {
  93.     delegate = theDelegate;
  94.     return self;
  95. }
  96.  
  97. /* set auto linefeed mode */
  98. - setAutoLineFeed:(BOOL)mode
  99. {
  100.     autoLf = mode;
  101.     return self;
  102. }
  103.  
  104. /* free object */
  105. - free
  106. {
  107.     [self killCommand];
  108.     return [super free];
  109. }
  110.  
  111. // --------------------------------------------------------------------------------
  112.  
  113. /* set font */
  114. - _setTextAttrFont:fontId color:(int)mode:(NXColor)color
  115. {
  116.     textAttr_t    tAttr = nullAttr;
  117.     tAttr.fontId    = fontId;
  118.     tAttr.colorMode    = mode;
  119.     tAttr.color        = color;
  120.     [self _appendTextAndMakeVisible:(char*)nil attr:tAttr];
  121.     return self;
  122. }
  123.  
  124. /* set font */
  125. - setTextAttributeFont:fontId
  126. {
  127.     return [self _setTextAttrFont:fontId color:0:nullColor];
  128. }
  129.  
  130. /* set gray */
  131. - setTextAttributeGray:(float)aGray
  132. {
  133.     return [self _setTextAttrFont:(id)nil color:1:NXConvertGrayToColor(aGray)];
  134. }
  135.  
  136. /* set gray */
  137. - setTextAttributeColor:(NXColor)aColor
  138. {
  139.     return [self _setTextAttrFont:(id)nil color:2:aColor];
  140. }
  141.  
  142. /* set default tabs */
  143. - setTabStops:(float*)tabArray count:(int)c
  144. {
  145.     NXTextStyle    style = *((NXTextStyle*)[textView defaultParaStyle]);
  146.     style.numTabs = (short)c;
  147.     style.tabs = (NXTabStop*)malloc(sizeof(NXTabStop) * style.numTabs);
  148.     while (--c >= 0) { style.tabs[c].kind = NX_LEFTTAB; style.tabs[c].x = tabArray[c]; }
  149.     [textView setParaStyle:(void*)&style];
  150.     return self;
  151. }
  152.  
  153. /* repeat given tab multiple times */
  154. - setTab:(float)tabSize count:(int)c
  155. {
  156.     int            i;
  157.     NXTextStyle    style = *((NXTextStyle*)[textView defaultParaStyle]);
  158.     style.numTabs = (short)c;
  159.     style.tabs = (NXTabStop*)malloc(sizeof(NXTabStop) * style.numTabs);
  160.     for (i = 0; i < c; i++) {
  161.         style.tabs[i].kind = NX_LEFTTAB;
  162.         style.tabs[i].x = (float)(i + 1) * tabSize;
  163.     }
  164.     [textView setParaStyle:(void*)&style];
  165.     return self;
  166. }
  167.  
  168. // --------------------------------------------------------------------------------
  169.  
  170. /* clear text scroll view area */
  171. - clearScrollText
  172. {
  173.     [textView setEditable:YES];
  174.     [textView setText:""];
  175.     if (!wasEditable) [textView setEditable:NO];
  176.     [scrollView display];
  177.     return self;
  178. }
  179. - clear:sender
  180. {
  181.     return [self clearScrollText];
  182. }
  183.     
  184. // --------------------------------------------------------------------------------
  185. // append text to scroll view
  186.  
  187. /* append buffer to view: return YES if text was visible */
  188. - (BOOL)_appendTextToView:(const char*)buffer len:(int)len attr:(textAttr_t)tAttr
  189. {
  190.     int        txtLen;
  191.     NXSelPt    startPt, endPt;
  192.     NXRect    rect;
  193.     
  194.     /* check for font/gray change (save state) */
  195.     if (tAttr.fontId) {
  196.         runAttr.fontId = tAttr.fontId;
  197.     }
  198.     if (tAttr.colorMode) {
  199.         runAttr.colorMode = tAttr.colorMode;
  200.         runAttr.color = tAttr.color;
  201.     }
  202.     if (!buffer || !*buffer || (len == 0)) return NO;
  203.     
  204.     /* get ready to print text */
  205.     [textView getVisibleRect:&rect];                      // visible rectangle
  206.     [textView setEditable:YES];
  207.     txtLen = [textView textLength];
  208.     [textView setSel:txtLen :txtLen];
  209.     [textView getSel:&startPt :&endPt];                   // selected coordinates
  210.  
  211.     /* set text run attributes if specified */
  212.     if (!isNullAttr(runAttr)) {
  213.         if ([textView isMonoFont]) [textView setMonoFont:NO];
  214.         if (!txtLen) { [textView replaceSel:" "]; [textView setSel:0 :1]; }
  215.         if (runAttr.fontId) [textView setSelFont:runAttr.fontId];
  216.         if (runAttr.colorMode == 1) [textView setSelGray:cvtColor2Gray(runAttr.color)]; else
  217.         if (runAttr.colorMode == 2) [textView setSelColor:runAttr.color];
  218.         runAttr = nullAttr;
  219.     }
  220.     
  221.     /* print text */
  222.     if (len > 0) [textView replaceSel:buffer length:len];
  223.     else [textView replaceSel:buffer];
  224.     if (!wasEditable) [textView setEditable:NO];
  225.     
  226.     return (rect.origin.y + rect.size.height > endPt.y);  // was visible?
  227. }
  228.  
  229. /* append text to view and scroll to visible */
  230. - _appendTextAndMakeVisible:(const char*)buffer attr:(textAttr_t)tAttr
  231. {
  232.     BOOL    wasVisible;
  233.   
  234.     /* print buffer */
  235.     wasVisible = [self _appendTextToView:buffer len:-1 attr:tAttr];
  236.     if (autoLf && buffer) [self _appendTextToView:"\n" len:-1 attr:nullAttr];
  237.     if (wasVisible) [textView scrollSelToVisible];
  238.   
  239.     return self;
  240. }
  241.  
  242. // --------------------------------------------------------------------------------
  243. // append a formatted text string message into a text view
  244.  
  245. /* append text unformatted */
  246. - (int)textPrint:(const char*)buffer
  247. {
  248.     [self _appendTextAndMakeVisible:buffer attr:nullAttr];
  249.     return strlen(buffer);
  250. }
  251.  
  252. /* append text with variable args into textView */
  253. - (int)textPrintf:(const char*)fmt args:(va_list)args
  254. {
  255.     char    tempString[textStringSIZE] = { 0 };
  256.     int        retVal = vsprintf(tempString, fmt, args);
  257.     [self _appendTextAndMakeVisible:tempString attr:nullAttr];
  258.     return retVal;
  259. }
  260.  
  261. /* append text with variable args into textView */
  262. - (int)textPrintf:(const char*)fmt, ...
  263. {
  264.     va_list        args;
  265.     int            retVal;
  266.     va_start(args, fmt);
  267.     retVal = [self textPrintf:fmt args:args];     
  268.     va_end(args);
  269.     return retVal;
  270. }
  271.   
  272. // --------------------------------------------------------------------------------
  273. // executing commands within a shell, using the scroll text as output
  274.  
  275. /* stop command */
  276. - _stopCommand
  277. {
  278.     int    error;
  279.     DPSRemoveFD(inputDescriptor);
  280.     error = [self _pclose];
  281.     close(inputDescriptor);
  282.     [self commandDidCompleteWithError:error];
  283.     return self;
  284. }
  285.  
  286. /* open pipe to shell and execute command */
  287. - (int)_popen:(const char*)cmd
  288. {
  289.     int            inputP[2], hisOutput, myInput;
  290.     const char    **locEnv = environ;
  291.     
  292.     /* open pipe */
  293.     pipe(inputP);
  294.     myInput = inputP[0];
  295.     hisOutput = inputP[1];
  296.     
  297.     /* fork and execute shell */
  298.     if ((cmdChild = vfork()) == 0) {
  299.         int i;
  300.         char **env;
  301.         
  302.         /* make local copy of environment table */
  303.         for (i = 0; locEnv[i]; i++);
  304.         env = (char**)alloca(sizeof(char*) * (i + 1));            // allocate on stack
  305.         memcpy(env, locEnv, sizeof(char*) * (i + 1));
  306.         /* modify environment as desired here */
  307.         
  308.         /* set up pipe handles */
  309.         close(myInput);
  310.         if (hisOutput != 1) dup2(hisOutput, 1);
  311.         if (hisOutput != 2) dup2(hisOutput, 2);
  312.         if ((hisOutput != 1) && (hisOutput != 2)) close(hisOutput);
  313.         
  314.         /* execute command */
  315.         setpgrp(0, getpid());
  316.         execle("/bin/csh", "csh", "-f", "-c", cmd, (char *)nil, env);
  317.         _exit(RUNCMD_EXEC);
  318.         
  319.     }
  320.     
  321.     /* set io */
  322.     if (cmdChild == -1)  { close(myInput); myInput = -1; }
  323.     close(hisOutput);
  324.     
  325.     return myInput;
  326. }
  327.  
  328. /* close shell command pipe */
  329. - (int)_pclose
  330. {
  331.     int            pid, omask, err;
  332.     union wait    status;
  333.     omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
  334.     while ((pid = wait(&status)) != cmdChild && pid != -1);
  335.     (void)sigsetmask(omask);
  336.     return (err = (status.w_status & 0xFF))? -1 : (status.w_status >> 8) & 0xFF;
  337. }
  338.  
  339. /* filter for data piped from command shell */
  340. - (void)_gotData
  341. {
  342.     char    data[1024];
  343.     int        n, cnt = 0;
  344.     BOOL    doScroll = NO;
  345.  
  346.     /* read available text */
  347.     do {
  348.         if ((n = read(inputDescriptor, data, sizeof(data))) >= 0) {
  349.             cnt += n;
  350.             if (n && [self _appendTextToView:data len:n attr:nullAttr]) doScroll = YES;
  351.         }
  352.     } while (n == sizeof(data));
  353.  
  354.     /* show text */
  355.     if (doScroll) [textView scrollSelToVisible];
  356.     if (!cnt) [self _stopCommand];
  357.     
  358. }
  359.  
  360. /* fd routine for receiving data */
  361. static void gotData(int fd, void *self)
  362. {
  363.     [(ExecScrollText*)self _gotData];
  364. }
  365.  
  366. /* execute a command */
  367. - runCommand:(const char*)command
  368. {
  369.     inputDescriptor = [self _popen:command];
  370.     if (inputDescriptor >= 0) {
  371.         DPSAddFD(inputDescriptor, gotData, self, NX_BASETHRESHOLD);
  372.         return self;
  373.     }
  374.     return (id)nil;
  375. }
  376.  
  377. /* terminate command */
  378. - terminateCommand
  379. {
  380.     if (cmdChild > 0) {
  381.         killpg(cmdChild, SIGTERM);
  382.         kill(cmdChild, SIGTERM);
  383.     }
  384.     return self;
  385. }
  386.   
  387. /* kill command */
  388. - killCommand
  389. {
  390.     if (cmdChild > 0) {
  391.         killpg(cmdChild, SIGKILL);
  392.         kill(cmdChild, SIGKILL);
  393.     }
  394.     return self;
  395. }
  396.  
  397. // --------------------------------------------------------------------------------
  398. // support for remote shell server output
  399.  
  400. /* copy text to scrollView (MAIN THREAD ONLY!) */
  401. - (oneway void)commandOutput:(const char*)buffer len:(int)len
  402. {
  403.     if (len && buffer) {
  404.         if ([self _appendTextToView:buffer len:len attr:nullAttr])
  405.             [textView scrollSelToVisible];
  406.         free((char*)buffer);
  407.     }
  408. }
  409.  
  410. /* indicate that the shell has completed */
  411. - (oneway void)commandDidCompleteWithError:(int)errorCode;
  412. {
  413.     if (delegate && [delegate respondsTo:@selector(commandDidComplete:withError:)])
  414.         [delegate commandDidComplete:self withError:errorCode];
  415. }
  416.  
  417. @end
  418.